﻿using System;
using System.Globalization;
using OpenTap.Plugins.Interfaces.ACPsu;
using OpenTap.Plugins.Interfaces.Common;

namespace OpenTap.Plugins.AcPsu
{
    [Display("Kikusui PCR-M Serial AC PSU driver", Group: "OpenTap.Plugins", Description: "Kikusui PCR500M/PCR1000M/PCR2000M/PCR4000M AC PSU driver")]
    public class AcPsuKikusui : AcPsuBase
    {

        #region Settings

        [Display(Name: "VoltageMode AC/DC/ACDC", Group: "Initial Parameters", Order: 1,
            Description: "Select voltage output under AC or DC mode.")]
        public EVoltageMode VoltageMode { get; set; }

        [Display(Name: "Voltage Range", Group: "Initial Parameters", Order: 2,
            Description: "Select voltage range 135V/270V/AUTO.")]
        public EVoltageRange VoltageRange { get; set; }

        #endregion

        public AcPsuKikusui()
        {
            VoltageMode = EVoltageMode.Ac;
            VoltageRange = EVoltageRange.RangeAuto;
            Name = "KikPCRSerial";
        }
        /// <inheritdoc />
        public override void Open()
        {
            if (IsConnected) throw new InvalidOperationException("Psu is already connected!");
            base.Open();
            Reset();

            var idString = IdnString;
            if (!idString.Contains("KIKUSUI,PCR")) throw new ApplicationException("Invalid IDN-query response! ('" + idString + "')");

            SetVoltageMode(VoltageMode);
            SetVoltageRange(VoltageRange);
        }

        public override double GetCurrentLimitDc()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac== VoltageMode)
                throw new ApplicationException("Read DC current limit error, psu under AC mode.");

            return ScpiQuery<double>("CURR:OFFS?");

        }
        public override double GetVoltageLevelDc()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC voltage error, psu under AC mode.");

            return ScpiQuery<double>("VOLT:OFFS?");

        }
        public override double GetVoltageDcProtectionLimitLower()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC voltage low limit error, psu under AC mode.");

            return ScpiQuery<double>("VOLT:OFFS:LIM:LOW?");
        }
        public override double GetVoltageDcProtectionLimitUpper()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC voltage upper limit error, psu under AC mode.");

            return ScpiQuery<double>("VOLT:OFFS:LIM:UPP?");
        }
        public override EState GetVoltageDcProtectionState()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC voltage protected status error, psu under AC mode.");

            return ScpiQuery<string>("VOLT:OFFS:LIM?") == "1" ? EState.On : EState.Off;
        }
        public override EVoltageMode GetVoltageMode()
        {
            CheckIfConnected();
            var mode = ScpiQuery("OUTP:COUP?").Trim('\n', '\r');

            if (mode.Equals("AC", StringComparison.InvariantCultureIgnoreCase))
                return EVoltageMode.Ac;
            if (mode.Equals("DC", StringComparison.InvariantCultureIgnoreCase))
                return EVoltageMode.Dc;
            if (mode.Equals("ACDC", StringComparison.InvariantCultureIgnoreCase))
                return EVoltageMode.AcDc;

            throw new ApplicationException("Error when reading voltage mode from instrument.");
        }

        public override double MeasureCurrentDc()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC current error, psu under AC mode.");

            return ScpiQuery<double>("MEAS:CURR:DC?");
        }
        public override double MeasureVoltageDc()
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Read DC voltage error, psu under AC mode.");

            return ScpiQuery<double>("MEAS:VOLT:DC?");
        }

        /// <inheritdoc />
        public override void SetCurrent(double currentAmps)
        {
            CheckIfConnected();
            if (EVoltageMode.Dc == VoltageMode)
                throw new ApplicationException("Set AC current error, psu under DC mode.");

            double minCurrentA = ScpiQuery<double>("CURR? MIN");
            if (currentAmps < minCurrentA)
                throw new ApplicationException("Current set value(" + currentAmps + ") is too low. MINimum is (" + minCurrentA + ")");

            double maxCurrentA = ScpiQuery<double>("CURR? MAX");
            if (currentAmps > maxCurrentA)
                throw new ApplicationException("Current set value(" + currentAmps + ") is too high. MAXimum is (" + maxCurrentA + ")");

            ScpiCommand("CURR " + currentAmps.ToString("#.000", CultureInfo.InvariantCulture));
        }

        public override void SetCurrentDc(double currentAmps)
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Set DC current error, psu under AC mode.");

            double minCurrentA = ScpiQuery<double>("CURR:OFFS? MIN");
            if (currentAmps < minCurrentA)
                throw new ApplicationException("Current set value(" + currentAmps + ") is too low. MINimum is (" + minCurrentA + ")");

            double maxCurrentA = ScpiQuery<double>("CURR:OFFS? MAX");
            if (currentAmps > maxCurrentA)
                throw new ApplicationException("Current set value(" + currentAmps + ") is too high. MAXimum is (" + maxCurrentA + ")");

            ScpiCommand("CURR:OFFS " + currentAmps.ToString("#.000", CultureInfo.InvariantCulture));

        }
        public override void SetVoltageDc(double voltageDc)
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Set DC voltage error, psu under AC mode.");

            if(VoltageRange == EVoltageRange.RangeLoV && voltageDc > Convert.ToInt32(EVoltageRange.RangeLoV))
                throw new ApplicationException("Voltage set vale (" + voltageDc + "V) higher than low range ("
                                               + Convert.ToInt32(EVoltageRange.RangeLoV) + "V).");

            if(voltageDc > Convert.ToInt32(EVoltageRange.RangeHiV))
                throw new ApplicationException("Voltage set vale (" + voltageDc + "V) higher than high/auto range ("
                                               + Convert.ToInt32(EVoltageRange.RangeHiV) + "V).");


            ScpiCommand("VOLT:OFFS " + voltageDc.ToString("#.000", CultureInfo.InvariantCulture));
        }
        public override void SetVoltageDcProtectionLimitLower(double voltageDcLimitLow)
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Set DC voltage limit error, psu under AC mode.");

            ScpiCommand("VOLT:OFFS:LIM:LOW " + voltageDcLimitLow.ToString("#.000", CultureInfo.InvariantCulture));
        }
        public override void SetVoltageDcProtectionLimitUpper(double voltageDcLimitUpp)
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Set DC voltage limit error, psu under AC mode.");

            ScpiCommand("VOLT:OFFS:LIM:UPP " + voltageDcLimitUpp.ToString("#.000", CultureInfo.InvariantCulture));
        }
        public override void SetVoltageDcProtectionState(EState onOff)
        {
            CheckIfConnected();
            if (EVoltageMode.Ac == VoltageMode)
                throw new ApplicationException("Set DC voltage protection error, psu under AC mode.");

            ScpiCommand("VOLT:OFFS:LIM " + onOff);
        }
        public override void SetVoltageMode(EVoltageMode voltageMode)
        {
            if (voltageMode == GetVoltageMode())
                return;

            // turn power off is mandatory if want to set voltage mode
            if (ScpiQuery("OUTP?").Trim('\n','\r').Contains("1"))
                ScpiCommand("OUTP OFF");

            switch (voltageMode)
            {
                case EVoltageMode.Ac:
                    ScpiCommand("OUTP:COUP AC");
                    break;
                case EVoltageMode.Dc:
                    ScpiCommand("OUTP:COUP DC");
                    break;
                case EVoltageMode.AcDc:
                    ScpiCommand("OUTP:COUP ACDC");
                    break;
                default:
                    throw new ArgumentException("Wrong voltage mode selected.");

            }
            VoltageMode = voltageMode;
            Log.Info("Set voltage output mode to {0}", voltageMode.ToString());
        }

        public override void SetVoltageRange(int voltageRangeV)
        {
            if(voltageRangeV<0)
                throw new ArgumentException("Voltage range should be 135V/270V.");
            if(voltageRangeV > Convert.ToInt32(EVoltageRange.RangeHiV))
                throw new ApplicationException("Voltage higher than HIGH range limit");

            if (voltageRangeV > Convert.ToInt32(EVoltageRange.RangeLoV))
                SetVoltageRange(EVoltageRange.RangeHiV);
            else
                SetVoltageRange(EVoltageRange.RangeLoV);
        }

        private void SetVoltageRange(EVoltageRange voltageRange)
        {
            if(EVoltageRange.RangeAuto == voltageRange)
                ScpiCommand("VOLT:RANG:AUTO ON");
            else
            {
                ScpiCommand("VOLT:RANG:AUTO OFF");
                ScpiCommand("VOLT:RANG " + Convert.ToInt32(voltageRange));
            }
        }
    }
}
